package com.andova.glcamera.texture

import android.content.Context
import android.graphics.SurfaceTexture
import android.opengl.GLES11Ext
import android.opengl.GLES30
import com.andova.egl.*
import com.andova.ext.d
import com.andova.ext.e
import com.andova.ext.getAsset
import com.andova.glcamera.*
import com.andova.glcamera.device.CAMERA_ID_0
import com.andova.glcamera.device.CAMERA_ID_1
import com.andova.glcamera.device.CameraCallback
import com.andova.image.glesInitialize
import com.andova.image.glesLogExtension
import com.andova.libyuv.scaleRgba

internal class TextureRender(
    private val context: Context, private val callback: TextureFrameCallback?,
    private val device: CameraCallback, private val egl: EglTextureCallback
) : OnTextureListener, OnTextureBridger {
    init {
        d("init TextureRender> context:$context")
    }

    private var oesProgramId = 0
    private var fboProgramId = 0
    private val fboIds = IntArray(1)
    private val textureIds = IntArray(2)

    private var surfaceWidth = 0
    private var previewWidth = 0
    private var surfaceHeight = 0
    private var previewHeight = 0
    private val calculator = Calculator()

    @FrameAction
    private var frameAction = FRAME_ACTION_NORMAL
    private var colorFormat = DEFAULT_COLOR_FORMAT
    private var delayMillisForStart = 0L

    @Volatile
    private var mStatus = 1 // -1:pause 0:pause&clear 1:resume
    private val drawer = Drawer()
    private val transformMatrix = FloatArray(16)
    private var frameHandler: GlFrameHThread? = null
    private var mSurfaceTexture: SurfaceTexture? = null
    private var mX: Float = 1f
    private var mY: Float = 1f

    private var xFlipScale = 1f
    private var yFlipScale = 1f
    private var sleepMillisecond = 0L
    private var recentMillisecond = 0L
    private var rotRadian = 0.0

    @Volatile
    private var lazyUpdated = true

    @Volatile
    private var taskResult: Any? = null

    @Volatile
    private var handlerResult: Any? = null

    @Volatile
    private var mBuffer: ByteArray? = null

    private fun toggleCamera(open: Boolean) = when {
        open && !device.isCameraOpened() -> {
            mSurfaceTexture = egl.onLoadTexture(textureIds[0])
            device.openCamera(mSurfaceTexture, device.getBuilder()) // todo 注意 先前就因为没有传第二个参数 导致图像一直拉伸变形没找到原因
        }
        !open && device.isCameraOpened() -> device.stopPreview()
        else -> null
    }

    private fun updateViewport(width: Int, height: Int) {
        previewWidth = width
        previewHeight = height
        val viewport = calculator.calcPreviewViewport(previewWidth, previewHeight, surfaceWidth, surfaceHeight)
        GLES30.glViewport(viewport.x, viewport.y, viewport.width, viewport.height)
    }

    override fun pause(clearColor: Boolean) = run { mStatus = if (clearColor) 0 else -1 }
    override fun resume() {
        mStatus = 1
        egl.onResumeTexture()
    }

    override fun start(delayMillis: Long, format: Int) {
        delayMillisForStart = delayMillis
        colorFormat = when (format) {
            GLES30.GL_RGB -> GLES30.GL_RGB
            GLES30.GL_RGB8 -> GLES30.GL_RGB8
            GLES30.GL_RGBA -> GLES30.GL_RGBA
            GLES30.GL_RGB565 -> GLES30.GL_RGB565
            else -> DEFAULT_COLOR_FORMAT
        }
        device.setBridger(this)
    }

    override fun action(command: Int) = run { frameAction = command }
    override fun handler(handler: GlFrameHThread?) = run { frameHandler = handler }
    override fun x(x: Float) = run { mX = x }
    override fun y(y: Float) = run { mY = y }
    override fun obtainAction(): Int = frameAction
    override fun onPreviewFrame(buffer: ByteArray, format: Int) {
        mBuffer = buffer
    }

    override fun flipScale(xFlipScale: Float, yFlipScale: Float) {
        this.xFlipScale = xFlipScale
        this.yFlipScale = yFlipScale
    }

    /**
     * 旋转弧度
     * @see Math.PI
     */
    override fun rotateRadian(radian: Double) {
        rotRadian = radian
    }

    override fun lazyCallback(sleepMillisecond: Long) {
        if (sleepMillisecond < 0) {
            this.sleepMillisecond = 0
        } else this.sleepMillisecond = sleepMillisecond
    }

    override fun onAvailable(width: Int, height: Int) {
        surfaceWidth = width
        surfaceHeight = height
        drawer.bind(width, height, colorFormat, mX, mY)

        GLES30.glClearColor(1f, 1f, 1f, 1f)
        GLES30.glDeleteTextures(textureIds.size, textureIds, 0)
        GLES30.glGenTextures(textureIds.size, textureIds, 0)
        GLES30.glDeleteFramebuffers(fboIds.size, fboIds, 0)
        GLES30.glGenFramebuffers(fboIds.size, fboIds, 0)

        glesLogExtension()

        // oes texture
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIds[0])//绑定到外部纹理上
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST.toFloat())
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR.toFloat())
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE.toFloat())
        GLES30.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE.toFloat())
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES30.GL_NONE)//解除纹理绑定

        if (delayMillisForStart <= 0) toggleCamera(true) else (egl as? MultiThreadEglHelper)?.sendForegroundMessageDelayed(1, delayMillisForStart)

        // fbo texture
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[1])
        GLES30.glTexImage2D(
            GLES30.GL_TEXTURE_2D, 0,
            when (colorFormat) {
                GLES30.GL_RGB, GLES30.GL_RGB565 -> GLES30.GL_RGB
                GLES30.GL_RGBA -> GLES30.GL_RGBA
                GLES30.GL_RGB8 -> GLES30.GL_RGB8
                else -> GLES30.GL_RGBA
            },
            width, height, 0,
            when (colorFormat) {
                GLES30.GL_RGB, GLES30.GL_RGB565 -> GLES30.GL_RGB
                GLES30.GL_RGBA -> GLES30.GL_RGBA
                GLES30.GL_RGB8 -> GLES30.GL_RGB8
                else -> GLES30.GL_RGBA
            },
            when (colorFormat) {
                GLES30.GL_RGB565 -> GLES30.GL_UNSIGNED_SHORT_5_6_5
                GLES30.GL_RGB, GLES30.GL_RGBA -> GLES30.GL_UNSIGNED_BYTE
                GLES30.GL_RGB8 -> GLES30.GL_UNSIGNED_BYTE
                else -> GLES30.GL_UNSIGNED_BYTE
            },
            null
        )//这个不能少
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_NEAREST.toFloat())
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR.toFloat())
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_S, GLES30.GL_CLAMP_TO_EDGE.toFloat())
        GLES30.glTexParameterf(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_WRAP_T, GLES30.GL_CLAMP_TO_EDGE.toFloat())

        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboIds[0])
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, textureIds[1], 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, GLES30.GL_NONE)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

        oesProgramId = glesInitialize(getAsset(context, "vert_texture_oes.glsl"), getAsset(context, "frag_texture_oes.glsl"))
        fboProgramId = glesInitialize(getAsset(context, "vert_texture_camera.glsl"), getAsset(context, "frag_texture_camera.glsl"))
        callback?.onSurfaceAvailable(width, height)
    }

    override fun onSizeChanged(width: Int, height: Int) {
        surfaceWidth = width
        surfaceHeight = height
        drawer.bind(width, height, colorFormat, mX, mY)
        callback?.onSurfaceSizeChanged(width, height)
    }

    override fun onDestroyed(surfaceTexture: SurfaceTexture?) {
        frameHandler = null
        GLES30.glDeleteProgram(oesProgramId)
        GLES30.glDeleteProgram(fboProgramId)
        GLES30.glDeleteTextures(textureIds.size, textureIds, 0)
        GLES30.glDeleteFramebuffers(fboIds.size, fboIds, 0)
        drawer.unbind()
        toggleCamera(false)
        callback?.onSurfaceDestroyed(surfaceTexture)
    }

    override fun onTextureFrame(surfaceTexture: SurfaceTexture?) {
        when (mStatus) {
            -1 -> return
            0 -> {
                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
                GLES30.glClearColor(0f, 0f, 0f, 0f)
                return
            }
        }
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        GLES30.glClearColor(0f, 0f, 0f, 0f)
        try {
            surfaceTexture?.updateTexImage() // 更新纹理图像
        } catch (e: IllegalStateException) {
            e("catch exception when onTextureFrame: $e")
            return
        }

        if (sleepMillisecond > 0L) {
            when (frameAction) {
                FRAME_ACTION_LAZY_NORMAL -> {
                    lazyUpdated = false
                    if (System.currentTimeMillis() - recentMillisecond >= sleepMillisecond) {
                        lazyUpdated = true
                        recentMillisecond = System.currentTimeMillis()
                    }
                }
                FRAME_ACTION_LAZY_ALL, FRAME_ACTION_LAZY_PREVIEW, FRAME_ACTION_LAZY_BUFFER -> {
                    lazyUpdated = false
                    Thread.sleep(sleepMillisecond)
                    lazyUpdated = true
                }
                else -> lazyUpdated = true
            }
        }

        surfaceTexture?.getTransformMatrix(transformMatrix)
        updateViewport(device.getPreviewSize().width, device.getPreviewSize().height)

        GLES30.glUseProgram(oesProgramId)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIds[0])
        val oesMatLoc = GLES30.glGetUniformLocation(oesProgramId, "uTexMat")
        val oesRadLoc = GLES30.glGetUniformLocation(oesProgramId, "uRotRad")
        val oesFliLoc = GLES30.glGetUniformLocation(oesProgramId, "uFlipScale")
        val oesSamLoc = GLES30.glGetUniformLocation(oesProgramId, "uTexSampler")
        val oesAutoPos = GLES30.glGetUniformLocation(oesProgramId, "uAutoTexPos")
        GLES30.glUniformMatrix4fv(oesMatLoc, 1, false, transformMatrix, 0)
        GLES30.glUniform1i(oesSamLoc, 0) // 0->gl_texture0
        GLES30.glUniform1f(oesRadLoc, rotRadian.toFloat())
        GLES30.glUniform2f(oesFliLoc, xFlipScale, yFlipScale)
        GLES30.glUniform1i(oesAutoPos, if (mX == 1f && mY == 1f) GLES30.GL_TRUE else GLES30.GL_FALSE)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboIds[0])
        drawer.drawFrameByVBO(0, 1)
        if (false) {
            when (frameAction) {
                FRAME_ACTION_BUFFER, FRAME_ACTION_LAZY_BUFFER, FRAME_ACTION_LAZY_NORMAL, FRAME_ACTION_NORMAL -> drawer.readPixels(surfaceWidth, surfaceHeight, colorFormat)
            }
        }
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE) // 一定要在这里结束绑定
        GLES30.glUseProgram(GLES30.GL_NONE)

        when (frameAction) {
            FRAME_ACTION_LAZY_BUFFER, FRAME_ACTION_BUFFER -> return
        }

        GLES30.glUseProgram(fboProgramId)
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[1])
        val fboRadLoc = GLES30.glGetUniformLocation(fboProgramId, "aRotRad")
        val fboSamLoc = GLES30.glGetUniformLocation(fboProgramId, "uTexSampler")
        val fboFlipLoc = GLES30.glGetUniformLocation(fboProgramId, "aFlipScale")
        GLES30.glUniform1i(fboSamLoc, 0)
        /**
         * 由于相机已经设置好了显示方向，所以实际上是不需要旋转了，当然，这也是因为我那oes纹理坐标奇怪的设定有关
         * 当然，如果没有设置显示方向，一般是要旋转90度或者270度
         * @see android.hardware.Camera.setDisplayOrientation
         * @see Math.PI
         */
        GLES30.glUniform1f(fboRadLoc, rotRadian.toFloat())
        GLES30.glUniform2f(fboFlipLoc, xFlipScale, yFlipScale)
        drawer.drawFrameVertex(0)
        callback?.onTextureFrame(surfaceTexture, taskResult)
        taskResult = null
        GLES30.glUseProgram(GLES30.GL_NONE)
    }

    override fun onTaskFrame(surfaceTexture: SurfaceTexture?) {
        when (frameAction) {
            FRAME_ACTION_PREVIEW, FRAME_ACTION_LAZY_PREVIEW -> return
        }
        if (!lazyUpdated) return
        handlerResult = frameHandler?.obtainResult()
        mBuffer?.apply {
            taskResult = callback?.onTaskFrame(previewWidth, previewHeight, this, handlerResult)
            handlerResult = null
            if (frameHandler == null) return@apply
            when (device.getCameraId()) {
                CAMERA_ID_0 -> frameHandler?.setCamera0Buffer(previewWidth, previewHeight, this, taskResult)
                CAMERA_ID_1 -> frameHandler?.setCamera1Buffer(previewWidth, previewHeight, this, taskResult)
            }
        }
        return
        val scale = device.getBuilder()?.imageScale ?: 1f
        val rgba = scaleRgba(drawer.pixelBuffer?.array()?.clone() ?: return, surfaceWidth, surfaceHeight, scale) ?: return
        callback?.onTaskFrame((surfaceWidth * scale).toInt(), (surfaceHeight * scale).toInt(), rgba, handlerResult)
    }

    override fun onDelayTask(what: Int) {
        toggleCamera(true)
    }
}